package com.example.spider.util;

import cn.hutool.core.io.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.shoulder.core.context.AppInfo;
import org.shoulder.core.exception.CommonErrorCodeEnum;
import org.shoulder.core.util.AssertUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.support.CronTrigger;

import jakarta.annotation.Nonnull;
import java.io.File;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * 定时全量刷新，适合少量数据无数据库存储
 * new LZ4Codec()
 * ThreadPoolTaskScheduler
 * PeriodicTrigger
 */
@Slf4j
public class FileBasedCollection<C extends Collection<T>, T> implements Collection<T>, DisposableBean {
    private static Consumer<FileBasedCollection<?, ?>> registerConsumer;

    private final C collection;
    private final File storageFile;
    private final AtomicBoolean dirty = new AtomicBoolean(false);
    private String name = "fileSet";

    private final Trigger syncToPersistenceStorageTrigger;
    private static final Trigger periodOf10SecondTrigger = new CronTrigger("*/10 * * * * ?");

    private final ReentrantLock loadLock = new ReentrantLock();

    public FileBasedCollection(C coll, File storageFile) {
        this(coll, storageFile, periodOf10SecondTrigger);
    }

    public FileBasedCollection(C coll, File storageFile, Trigger syncToPersistenceStorageTrigger) {
        this.collection = coll;
        this.storageFile = storageFile;
        this.syncToPersistenceStorageTrigger = syncToPersistenceStorageTrigger;
    }

    public <COLL extends Collection<TT>, TT> FileBasedCollection<COLL, TT> create(COLL coll, File storageFile) {
        AssertUtils.notNull(registerConsumer, CommonErrorCodeEnum.ILLEGAL_STATUS, "context 未初始化，请使用 bean 注册形式使用.");
        FileBasedCollection<COLL, TT>  s = new FileBasedCollection<>(coll, storageFile);
        registerConsumer.accept(s);
        return s;
    }

    public static void setRegisterConsumer(Consumer<FileBasedCollection<?, ?>> c) {
        registerConsumer = c;
    }

    public void loadFromStorage() {
        loadLock.lock();
        try {
            if (storageFile.exists()) {
                List<String> list = FileUtil.readLines(storageFile, AppInfo.charset());
                List<T> loaded = list.stream().map(this::parseObject).collect(Collectors.toList());
                log.info("load FileBasedCollection({}, size={}) from storage: {}.", name, loaded.size(), storageFile);
                collection.addAll(loaded);
            }
        } finally {
            loadLock.unlock();
        }

    }

    //@PreDestroy
    public void syncToPersistenceStorage() {
        if (dirty.get()) {
            synchronized (this) {
                if (!dirty.get()) {
                    return;
                }
                dirty.set(false);
            }
            FileUtil.writeLines(collection, storageFile, AppInfo.charset());
            log.info("sync FileBasedCollection({}, size={}) to storage: {}.", name, collection.size(), storageFile);
        }
    }

    protected String toStr(T obj) {
        return (String) obj;
    }

    protected T parseObject(String line) {
        return (T) line;
    }

    public C getCollection() {
        return collection;
    }

    public File getStorageFile() {
        return storageFile;
    }

    public boolean isDirty() {
        return dirty.get();
    }

    // =======================================================


    @Override
    public int size() {
        return collection.size();
    }

    @Override
    public boolean isEmpty() {
        return collection.isEmpty();
    }

    @Override
    public boolean contains(Object o) {
        return collection.contains(o);
    }

    @Nonnull
    @Override
    public Iterator<T> iterator() {
        return collection.iterator();
    }

    @Nonnull
    @Override
    public Object[] toArray() {
        return collection.toArray();
    }

    @Nonnull
    @Override
    public <T1> T1[] toArray(@Nonnull T1[] a) {
        return collection.toArray(a);
    }

    @Override
    public boolean add(T t) {
        boolean added = collection.add(t);
        if (added) {
            dirty.set(true);
        }
        return added;
    }

    @Override
    public boolean remove(Object o) {
        boolean contains = collection.remove(o);
        if (contains) {
            dirty.set(true);
        }
        return contains;
    }

    @Override
    public boolean containsAll(@Nonnull Collection<?> c) {
        return collection.containsAll(c);
    }

    @Override
    public boolean addAll(@Nonnull Collection<? extends T> c) {
        boolean addAny = collection.addAll(c);
        if (addAny) {
            dirty.set(true);
        }
        return addAny;
    }

    @Override
    public boolean retainAll(@Nonnull Collection<?> c) {
        boolean changed = collection.retainAll(c);
        if (changed) {
            dirty.set(true);
        }
        return changed;
    }

    @Override
    public boolean removeAll(@Nonnull Collection<?> c) {
        boolean changed = collection.removeAll(c);
        if (changed) {
            dirty.set(true);
        }
        return changed;
    }

    @Override
    public void clear() {
        collection.clear();
        dirty.set(true);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Trigger getSyncToPersistenceStorageTrigger() {
        return syncToPersistenceStorageTrigger;
    }

    @Override
    public void destroy() throws Exception {
        syncToPersistenceStorage();
    }
}
